﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Nyamr.Implementation;
using System.IO;
using System.Text.RegularExpressions;

namespace Nyamr
{
    class Program
    {
        static void Desciption() 
        {
            Console.WriteLine("To create a new map/reduce plan use:");
            Console.WriteLine();
            Console.WriteLine("\tnyamr new_plan name");
            Console.WriteLine();
            Console.WriteLine("To execute a plan use:");
            Console.WriteLine();
            Console.WriteLine("\tnyamr plan-file-name");
            Console.WriteLine();
            Console.WriteLine("or:");
            Console.WriteLine();
            Console.WriteLine("\tnyamr");
            Console.WriteLine();
            Console.WriteLine("to run the default plan (plan.xml)");
            Console.WriteLine();
            Console.WriteLine("\tnyamr");
            Console.WriteLine();
            Console.WriteLine("\tnyamr show-dublicated-keys index");
            Console.WriteLine("\tnyamr clean index");
            Console.WriteLine("\tnyamr delete index key");
            Console.WriteLine();
            Console.WriteLine("To get statistic use");
            Console.WriteLine("\tnyamr statistics plan");
        }

        static void LoadPlanAndRun(string file) 
        {
            Plan plan = Plan.Load(file);
            
            try
            {
                Nyamr.Run(plan);
            }
            catch (Plan.InvalidDataException e)
            {
                Console.WriteLine(e.Message);
                throw;
            }
        }

        static void LoadPlanAndGetStatistics(string file)
        {
            Plan plan = Plan.Load(file);

            try
            {
                Nyamr.GetStatistics(plan);
            }
            catch (Plan.InvalidDataException e)
            {
                Console.WriteLine(e.Message);
                throw;
            }
        }

        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                LoadPlanAndRun("plan.xml");
            }
            else if (args.Length == 1)
            {
                LoadPlanAndRun(args[0]);
            }
            else if (args.Length == 2 && args[0] == "statistics")
            {
                LoadPlanAndGetStatistics(args[1]);
            }
            else if (args.Length == 2 && args[0] == "new_plan")
            {
                new Plan().Save(args[1]);
            }
            else if (args.Length == 2 && args[0] == "show-dublicated-keys")
            {
                var indexFile = Path.Combine(args[1], "index.data");
                var index = IndexItem.Load(indexFile);
                var dublicated = new HashSet<string>();
                var known = new HashSet<string>();
                foreach (var item in index)
                {
                    if (known.Contains(item.Key))
                    {
                        dublicated.Add(item.Key);
                    }
                    known.Add(item.Key);
                }
                foreach (var key in dublicated)
                {
                    Console.WriteLine(key);
                }
            }
            else if (args.Length == 2 && args[0] == "clean")
            {
                var indexFile = Path.Combine(args[1], "index.data");
                var index = IndexItem.Load(indexFile);

                var known = new HashSet<string>();
                foreach (var item in index)
                {
                    known.Add(item.Key);
                }

                var data = new DirectoryInfo(Path.Combine(args[1], "data"));
                foreach (var file in data.GetFiles())
                {
                    if (!known.Contains(file.Name))
                    {
                        Console.WriteLine("Deleting {0}", file);
                        file.Delete();
                    }
                }
            }
            else if (args.Length == 3 && args[0] == "delete")
            {
                var key = args[2];
                var index = new List<IndexItem>();
                var indexFile = Path.Combine(args[1], "index.data");
                var dataDir = Path.Combine(args[1], "data");
                foreach (var item in IndexItem.Load(indexFile))
                {
                    if (item.Key == key)
                    {
                        File.Delete(Path.Combine(dataDir, item.ValueFileName));
                    }
                    else
                    {
                        index.Add(item);
                    }
                }
                File.Delete(indexFile);
                IndexItem.Save(indexFile, index);
            }
            else if (args.Length == 3 && args[0] == "test")
            {
                var settings = args[1];
                var key = args[2];
                var plan = Plan.Load(settings);
                Nyamr.Test(plan, key);
            }
            else if (args.Length > 1 && args[0] == "sequence")
            {
                for (int i = 1; i < args.Length; i++)
                {
                    LoadPlanAndRun(args[i]);
                }
            }
            else if (args.Length == 4 && args[0] == "inject" && args[2] == "to")
            {
                var number = new Regex(@"(?<number>\d+)");
                
                var index = new List<IndexItem>();
                var whatIndexFile = Path.Combine(args[1], "index.data");
                var whatDataDir = Path.Combine(args[1], "data");

                var toIndexFileFoo = Path.Combine(args[3], "index.tmp");
                var toIndexFile = Path.Combine(args[3], "index.data");
                var toDataDir = Path.Combine(args[3], "data");

                var keyToOld = new Dictionary<string, IndexItem>();
                var max = 0;
                foreach (var key in IndexItem.Load(toIndexFile))
                {
                    if (keyToOld.ContainsKey(key.Key))
                    {
                        File.Delete(Path.Combine(toDataDir, keyToOld[key.Key].ValueFileName));
                        keyToOld.Remove(key.Key);
                    }
                    keyToOld.Add(key.Key, key);
                    var match = number.Match(key.ValueFileName);
                    if (match.Success)
                    {
                        max = Math.Max(max, int.Parse(match.Result("${number}")));
                    }
                }
                max++;
                
                foreach (var item in IndexItem.Load(whatIndexFile))
                {
                    if (keyToOld.ContainsKey(item.Key))
                    {
                        File.Delete(Path.Combine(toDataDir, keyToOld[item.Key].ValueFileName));
                        keyToOld.Remove(item.Key);
                    }
                    File.Copy(
                        Path.Combine(whatDataDir, item.ValueFileName),
                        Path.Combine(toDataDir, max.ToString() + ".data")
                    );
                    keyToOld.Add(item.Key, new IndexItem() 
                    { 
                        Key = item.Key,
                        ValueFileName = max.ToString() + ".data"
                    });
                    max++;
                }
                
                
                File.Delete(toIndexFileFoo);
                IndexItem.Save(toIndexFileFoo, keyToOld.Values.ToList());
                File.Delete(toIndexFile);
                File.Move(toIndexFileFoo, toIndexFile);
            }
            else Desciption();
        }
    }
}
